﻿/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Velocity4Net.Ctx;

namespace Velocity4Net.Runtime.Parse.AST
{
    /**
     * Helps handle math<br><br>
     *
     * Please look at the Parser.jjt file which is
     * what controls the generation of this class.
     *
     * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
     * @author <a href="mailto:pero@antaramusic.de">Peter Romianowski</a>
     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
     * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
     * @author Nathan Bubna
     * @version $Id: ASTMathNode.java 517553 2007-03-13 06:09:58Z wglass $
     */
    public abstract class ASTMathNode : SimpleNode
    {
        protected bool strictMode = false;

        public ASTMathNode(int id)
            : base(id)
        {

        }

        public ASTMathNode(Parser p, int id)
            : base(p, id)
        {
        }

        /**
         * {@inheritDoc}
         */
        public override Object Init(InternalContextAdapter context, Object data)
        {
            base.Init(context, data);
            strictMode = rsvc.GetBoolean(RuntimeConstants.STRICT_MATH, false);
            return data;
        }


        /**
         * gets the two args and performs the operation on them
         *
         * @param context
         * @return result or null
         * @throws MethodInvocationException
         */
        public override Object GetValue(InternalContextAdapter context)
        {
            Object left = jjtGetChild(0).GetValue(context);
            Object right = jjtGetChild(1).GetValue(context);

            /*
             * should we do anything special here?
             */
            Object special = HandleSpecial(left, right, context);
            if (special != null)
            {
                return special;
            }
            decimal l = 0;
            decimal r = 0;
            try
            {
                 l = Convert.ToDecimal(left);
                 r = Convert.ToDecimal(right);

            }
            catch (System.Exception e)
            {
                
                if (strictMode)
                {
                    log.error(e.Message);
                    throw new InvalidCastException(e.Message);
                }
                else
                {
                    log.debug(e.Message);
                    return null;
                }
            }
            /*
             * convert to Number if applicable
             */
            //if (left instanceof TemplateNumber)
            //{
            //   left = ((TemplateNumber)left).getAsNumber();
            //}
            //if (right instanceof TemplateNumber)
            //{
            //   right = ((TemplateNumber)right).getAsNumber();
            //}

            /*
             * if not a Number, not much we can do
             */
            //if (!(left instanceof Number) || !(right instanceof Number))
            //{
            //    boolean wrongright = (left instanceof Number);
            //    boolean wrongtype = wrongright ? right != null : left != null;
            //    String msg = (wrongright ? "Right" : "Left")
            //                + " side of math operation ("
            //                + jjtGetChild(wrongright ? 1 : 0).literal() + ") "
            //                + (wrongtype ? "is not a Number. " : "has a null value. ")
            //                + getLocation(context);
            //    if (strictMode)
            //    {
            //        log.error(msg);
            //        throw new MathException(msg);
            //    }
            //    else
            //    {
            //        log.debug(msg);
            //        return null;
            //    }
            //}

            return Perform(l, r, context);
        }

        /**
         * Extension hook to allow special behavior by subclasses
         * If this method returns a non-null value, that is returned,
         * rather than the result of the math operation.
         * @see ASTAddNode#handleSpecial
         */
        protected virtual Object HandleSpecial(Object left, Object right, InternalContextAdapter context)
        {
            // do nothing, this is an extension hook
            return null;
        }

        /**
         * Performs the math operation represented by this node.
         */
        public abstract decimal Perform(decimal left, decimal right, InternalContextAdapter context);

    }
}
